package image;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Range;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.core.Core.MinMaxLocResult;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;

public class FormDetector {

	private Mat untouched;
	private Mat draw;
	
	public FormDetector(String image) {
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
		untouched = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_COLOR);
		draw = untouched.clone();
	}

	public Mat getUntouched() { return untouched; }
	public void setUntouched(Mat untouched) { this.untouched = untouched; }

	public Mat getDraw() { return draw; }
	public void setDraw(Mat draw) { this.draw = draw; }

	/**
	 * Calcul la lookUpTable d'une forme.
	 * @param image
	 * @return
	 */
	public static double[] computeLUT(String image) {
		Mat m = null;
		Mat m_original = null;
		//Mat m_draw = null;
		Mat grad_x = null;
		Mat grad_y = null;
		Mat outline = null;
		Mat grad_angle = null;

		int[] LUT_x = new int[360];
		int[] LUT_y = new int[360];
		double[] LUT = new double[360];
		int[] LUT_nb_points = new int[360];
		
		Size size;
		
		int bary_i = 0;
		int bary_j = 0;
		
		// importer l'image de base
		m = Highgui.imread(image, Highgui.CV_LOAD_IMAGE_GRAYSCALE);
		m_original = Highgui.imread(image, Highgui.CV_LOAD_IMAGE_GRAYSCALE);
		//m_draw = Highgui.imread(image, Highgui.CV_LOAD_IMAGE_COLOR);
		m.convertTo(m, CvType.CV_64FC1);
		size = new Size(m.size().width, m.size().height);
		
		// espace de données entre 0 et 1
		/*for(int i=0; i<size.height; i++) {
			for(int j=0; j<size.width; j++) {
				m.put(i, j, m.get(i, j)[0]/255);
			}
		}*/
		
		// flouter
		Imgproc.GaussianBlur(m, m, new Size(9, 9), 0, 0);
		
		// calculer le contour
		List<MatOfPoint> liste_contours = new ArrayList<MatOfPoint>();
		Imgproc.findContours(m_original, liste_contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
		
		// parcourir les differents contours
		MatOfPoint contour  = liste_contours.get(0);
		//for(MatOfPoint contour : liste_contours) {
			bary_i = 0;
			bary_j = 0;
			
			// calcul du barycentre
			for(int i=0; i<contour.size().height; i++) {
				//Core.circle(m_draw, new Point(contour.get(i, 0)), 1, new Scalar(0, 0, 255));
				bary_i += contour.get(i, 0)[0];
				bary_j += contour.get(i, 0)[1];
			}
			bary_i /= contour.size().height;
			bary_j /= contour.size().height;
			//System.out.println("barycentre: "+bary_i+" "+bary_j);
			//Core.circle(m_draw, new Point(bary_i, bary_j), 1, new Scalar(0, 0, 255));
			
			// calcul des gradients
			grad_x = Mat.zeros(size, CvType.CV_64FC1);
			grad_y = Mat.zeros(size, CvType.CV_64FC1);
			for(int i=1; i<size.height-1; i++) {
				for(int j=1; j<size.width-1; j++) {
					grad_y.put(i, j, m.get(i+1, j)[0] - m.get(i-1, j)[0]);
					grad_x.put(i, j, m.get(i, j+1)[0] - m.get(i, j-1)[0]);
				}
			}
			
			// calcul des lookUpTables
			outline = Mat.zeros(size, CvType.CV_64FC1);
			grad_angle = Mat.zeros(size, CvType.CV_64FC1);
			for(int i=0; i<size.height; i++) {
				for(int j=0; j<size.width; j++) {
					double ix = -grad_x.get(i, j)[0];
					double iy = -grad_y.get(i, j)[0];
					
					if(Math.pow(ix, 2) + Math.pow(iy, 2) > 0.1) {
		    			outline.put(i, j, 1);
		    			
		    			double grad_norm_angle_origine = Math.atan2(iy,ix)/(2*Math.PI);
		    			double grad_norm_angle = (grad_norm_angle_origine+1) % (double) 1;
		    			
		    			int grad_angle_in_degree = (int) (grad_norm_angle*360);
		    			grad_angle.put(i, j, grad_norm_angle);

		    			LUT_x[grad_angle_in_degree] += (j- (int) bary_i);
		    			LUT_y[grad_angle_in_degree] += (i- (int) bary_j);
		    			LUT_nb_points[grad_angle_in_degree]++;
					}
				}
			}
			
			for(int i = 0;i<360; i++) {
		    	if(LUT_nb_points[i]>0) {
		    		double bary_angle_norm_original = Math.atan2(LUT_y[i]/LUT_nb_points[i], LUT_x[i]/LUT_nb_points[i]) / (2*Math.PI);
		    		double bary_angle_norm = (bary_angle_norm_original+1) % (double) 1;	
		    		LUT[i] = bary_angle_norm*360;
		    	} else {
		    		LUT[i] = -1;
		    	}
		    	
		    }
		
			saveLUT(image, LUT);
			return LUT;
	}
	
	/**
	 * Sauvegarde la lookUpTable dans un fichier.
	 * @param title
	 * @param LUT
	 */
	public static void saveLUT(String title, double[] LUT) {
		try {
			FileWriter fw = new FileWriter(new File(title+".txt"));
			BufferedWriter writer = new BufferedWriter(fw);
			for(int i=0; i<360; i++) {
				writer.write(String.valueOf((int) LUT[i]));
				writer.newLine();
			}
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Charge une lookUpTable pré-calculée.
	 * @param text
	 * @return
	 */
	public static double[] loadLUT(String text) {
		double[] LUT = new double[360];
		String tmp = "";
		
		try {
			FileReader fr = new FileReader(new File(text));
			BufferedReader reader = new BufferedReader(fr);
			for(int i=0; i<360; i++) {
				tmp = reader.readLine();
				LUT[i] = Integer.valueOf(tmp);
			}
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return LUT;
	}
	
	/**
	 * Détecte un visage sur une image et la retourne les coordonées de la zone contenant les yeux.
	 * @param image
	 * @return coord du rectangle contenant les yeux
	 */
	public static Point[] findFace(String image) {
		// lookUpTable de la forme d'un visage typique
		//double[] lut = FormDetector.computeLUT("image/Elipse.png");
		double[] lut = FormDetector.loadLUT("image/elipse.lut");
		
		// paramètre ajustables
		int blur = 3;
		int seuil_contours = 200;
		double r_min = 100;
		double r_max = 200;
		int direction_grad = 1; // gradient positif, visage clair sur fond foncé
		
		// matrice de base pour les calculs
		Mat m = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_GRAYSCALE);
		m.convertTo(m, CvType.CV_64FC1);
		
		// matrice pour les dessins
		Mat m_draw = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_COLOR);
		
		// image non modifiée
		Mat m_original = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_GRAYSCALE);
		
		Size size = new Size(m.size().width, m.size().height);
		
		// flouter
		Imgproc.GaussianBlur(m, m, new Size(blur, blur), 0, 0);
		
		// calculer les contours
		List<MatOfPoint> liste_contours = new ArrayList<MatOfPoint>();
		Imgproc.findContours(m_original, liste_contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_NONE);
		MatOfPoint contour = liste_contours.get(0);
		
		// calculer les gradients
		Mat grad_x = Mat.zeros(size, CvType.CV_64FC1);
		Mat grad_y = Mat.zeros(size, CvType.CV_64FC1);
		for(int i=1; i<size.height-1; i++) {
			for(int j=1; j<size.width-1; j++) {
				grad_y.put(i, j, m.get(i+1, j)[0] - m.get(i-1, j)[0]);
				grad_x.put(i, j, m.get(i, j+1)[0] - m.get(i, j-1)[0]);
			}
		}
		
		// parcourir les contours
		// pour chaque point calculer l'angle (direction du gradient)
		Mat accumulation = Mat.zeros(size, CvType.CV_64FC1);
		Mat outline = Mat.zeros(size, CvType.CV_64FC1);
		Mat grad_angle = Mat.zeros(size, CvType.CV_64FC1);
		for(int i=0; i<size.height; i++) {
			for(int j=0; j<size.width; j++) {
				double ix = direction_grad * grad_x.get(i, j)[0];
				double iy = direction_grad * grad_y.get(i, j)[0];

				if(Math.pow(ix, 2) + Math.pow(iy, 2) > seuil_contours) {
					//System.out.println(Math.pow(ix, 2) + Math.pow(iy, 2));
					outline.put(i, j, 1);
	    			//System.out.println("cord:\t"+i+" "+j);
					
					double grad_norm_angle = Math.atan2(iy,ix)/(2*Math.PI);
					grad_norm_angle = (grad_norm_angle+1) % (double) 1;
					int grad_angle_in_degree = (int) (grad_norm_angle*360);
					
					grad_angle.put(i, j, grad_norm_angle);
					
					// chercher l'angle beta avec la LUT
					double beta = getRadian(lut[grad_angle_in_degree]);
					//System.out.println("beta:\t"+lut_cercle[grad_angle_in_degree]+"\t"+beta);
					
					// tarcer la droite dans la matrice d'accumulation  entre r min et r max
					for(int k=(int) r_min; k<r_max; k++) {
						int x = (int) (j + Math.cos(beta) * k);
						int y = (int) (i + Math.sin(beta) * k);
						
						if(x >= 0 && y >= 0 && x < size.width && y < size.height)
							accumulation.put(y, x, accumulation.get(y, x)[0]+1);
					}
				}
			}
		}

		// calcul le maximum de la table d'accumulation (le centre de gravité du visage)
		MinMaxLocResult max = Core.minMaxLoc(accumulation);
		Core.circle(m_draw, max.maxLoc, 1, new Scalar(0, 0, 255), 3);
		
		// encadrer le visage
		int rect_size = (int) r_max;
		Point rect_1 = new Point(max.maxLoc.x-(rect_size/2), max.maxLoc.y-rect_size);
		Point rect_2 = new Point(max.maxLoc.x+(rect_size/2), max.maxLoc.y+rect_size);
		Core.rectangle(m_draw, rect_1, rect_2, new Scalar(0, 0, 255));
		
		// encadrer la zone des yeux
		rect_1 = new Point(max.maxLoc.x-(rect_size/2), max.maxLoc.y-(rect_size/4));
		rect_2 = new Point(max.maxLoc.x+(rect_size/2), max.maxLoc.y+(rect_size/4));
		Core.rectangle(m_draw, rect_1, rect_2, new Scalar(0, 255, 0));
		
		// images pour le debug
		/*new LoadImage("image/resultats/"+image+"_original.png", m_draw, "Original", false, 0, 0);
		new LoadImage("image/resultats/"+image+"_flou.png", m, "Flou", false, 320, 0);
		new LoadImage("image/resultats/"+image+"_outline.png", outline, "Contours", true, 640, 0);
		new LoadImage("image/resultats/"+image+"_grad_angle.png", grad_angle, "Direction du gradient", true, 0, 240);
		new LoadImage("image/resultats/"+image+"_acc.png", accumulation, "Accumulation", false, 320, 240);*/
		
		Point[] res = {rect_1, rect_2};
		return res;
	}
	
	public static Point[] findEye(String image, Point[] eye_zone) {
		// lookUpTable de la forme d'un oeil
		//double[] lut = FormDetector.computeLUT("image/circle_BW.png");
		double[] lut = FormDetector.loadLUT("image/disque.lut");
		
		// paramètre ajustables
		int blur = 3;
		int seuil_contours = 200;
		double r_min = 0;
		double r_max = 15;
		int direction_grad = -1; // gradient negatif, iris foncée sur fond de l'oeil blanc
		
		// matrice de base pour les calculs
		Mat m = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_GRAYSCALE);
		m.convertTo(m, CvType.CV_64FC1);
		
		// matrice pour les dessins
		Mat m_draw = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_COLOR);
		
		// image non modifiée
		Mat m_original = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_GRAYSCALE);
		
		Size size = new Size(m.size().width, m.size().height);
		
		// flouter
		Imgproc.GaussianBlur(m, m, new Size(blur, blur), 0, 0);
		
		// calculer les contours
		List<MatOfPoint> liste_contours = new ArrayList<MatOfPoint>();
		Imgproc.findContours(m_original, liste_contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_NONE);
		MatOfPoint contour = liste_contours.get(0);
		
		// calculer les gradients
		Mat grad_x = Mat.zeros(size, CvType.CV_64FC1);
		Mat grad_y = Mat.zeros(size, CvType.CV_64FC1);
		for(int i=1; i<size.height-1; i++) {
			for(int j=1; j<size.width-1; j++) {
				grad_y.put(i, j, m.get(i+1, j)[0] - m.get(i-1, j)[0]);
				grad_x.put(i, j, m.get(i, j+1)[0] - m.get(i, j-1)[0]);
			}
		}
		
		// parcourir les contours
		// pour chaque point calculer l'angle (direction du gradient)
		Mat accumulation = Mat.zeros(size, CvType.CV_64FC1);
		Mat outline = Mat.zeros(size, CvType.CV_64FC1);
		Mat grad_angle = Mat.zeros(size, CvType.CV_64FC1);
		for(int i=0; i<size.height; i++) {
			for(int j=0; j<size.width; j++) {
				double ix = direction_grad * grad_x.get(i, j)[0];
				double iy = direction_grad * grad_y.get(i, j)[0];

				if(Math.pow(ix, 2) + Math.pow(iy, 2) > seuil_contours
						&& j > eye_zone[0].x && j < eye_zone[1].x
						&& i > eye_zone[0].y && i < eye_zone[1].y) {
					//System.out.println(Math.pow(ix, 2) + Math.pow(iy, 2));
					outline.put(i, j, 1);
	    			//System.out.println("cord:\t"+i+" "+j);
					
					double grad_norm_angle = Math.atan2(iy,ix)/(2*Math.PI);
					grad_norm_angle = (grad_norm_angle+1) % (double) 1;
					int grad_angle_in_degree = (int) (grad_norm_angle*360);
					
					grad_angle.put(i, j, grad_norm_angle);
					
					// chercher l'angle beta avec la LUT
					double beta = getRadian(lut[grad_angle_in_degree]);
					//System.out.println("beta:\t"+lut_cercle[grad_angle_in_degree]+"\t"+beta);
					
					// tarcer la droite dans la matrice d'accumulation  entre r min et r max
					for(int k=(int) r_min; k<r_max; k++) {
						int x = (int) (j + Math.cos(beta) * k);
						int y = (int) (i + Math.sin(beta) * k);
						
						if(x >= 0 && y >= 0 && x < size.width && y < size.height)
							accumulation.put(y, x, accumulation.get(y, x)[0]+1);
					}
				}
			}
		}

		// calcul le maximum de la table d'accumulation (le centre de gravité du visage)
		MinMaxLocResult max = Core.minMaxLoc(accumulation);
		Core.circle(m_draw, max.maxLoc, 1, new Scalar(0, 0, 255), 3);
		
		// encadrer l'oeil
		int rect_size = (int) r_max;
		Point rect_1 = new Point(max.maxLoc.x-rect_size, max.maxLoc.y-rect_size);
		Point rect_2 = new Point(max.maxLoc.x+rect_size, max.maxLoc.y+rect_size);
		Core.rectangle(m_draw, rect_1, rect_2, new Scalar(0, 0, 255));
		
		// images pour le debug
		new LoadImage("image/resultats/"+image+"_original.png", m_draw, "Original", false, 0, 0);
		new LoadImage("image/resultats/"+image+"_flou.png", m, "Flou", false, 320, 0);
		new LoadImage("image/resultats/"+image+"_outline.png", outline, "Contours", true, 640, 0);
		new LoadImage("image/resultats/"+image+"_grad_angle.png", grad_angle, "Direction du gradient", true, 0, 240);
		new LoadImage("image/resultats/"+image+"_acc.png", accumulation, "Accumulation", false, 320, 240);
		
		Point[] res = {rect_1, rect_2};
		return res;
	}
	
	/**
	 * Retourne l'histogramme de la zone demandée sous la forme:
	 * r1 g1 b1 r2 g2 b2 r3...
	 * @param m
	 * @param zone
	 * @return
	 */
	// TODO convertir l'histo en int/long et non en char
	public static String computeHisto(Mat m, Point[] zone) {
		Mat m_sub = new Mat(m, new Range((int) zone[0].y, (int) zone[1].y),
				new Range((int) zone[0].x, (int) zone[1].x)).clone();
		
		List<Mat> m_color_split = new ArrayList<Mat>();
		Core.split(m_sub, m_color_split);
		
		MatOfInt channels = new MatOfInt(0);
		Mat hist_r = new Mat();
		Mat hist_g = new Mat();
		Mat hist_b = new Mat();
		MatOfInt histSize = new MatOfInt(256);

		float range[] = {0, 256};
		MatOfFloat ranges = new MatOfFloat(range);

		Imgproc.calcHist(m_color_split.subList(0, 1), channels, new Mat(), hist_r, histSize, ranges);
		Imgproc.calcHist(m_color_split.subList(1, 2), channels, new Mat(), hist_g, histSize, ranges);
		Imgproc.calcHist(m_color_split.subList(2, 3), channels, new Mat(), hist_b, histSize, ranges);
		
		new LoadImage("image/resultats/eye_for_histo.png", m_sub, "Eye", false, 0, 0);
		
		String res = "";
		for(int i=0; i<256; i++) {
			res = res + (char) hist_r.get(i, 0)[0];
			res = res + (char) hist_g.get(i, 0)[0];
			res = res + (char) hist_b.get(i, 0)[0];
		}
		return res;
	}
	
	public static double compareHistoEuclid(String histo1, String histo2) {
		int sum_r = 0;
		int sum_g = 0;
		int sum_b = 0;
		
		byte[] tmp1 = histo1.getBytes();
		byte[] tmp2 = histo2.getBytes();
		
		for(int i=0; i<256; i++) {
			sum_r += ((int) tmp1[i] - (int) tmp2[i]) * ((int) tmp1[i] - (int) tmp2[i]);
			sum_g += (tmp1[i+1] - tmp2[i+1]) * (tmp1[i+1] - tmp2[i+1]);
			sum_b += (tmp1[i+2] - tmp2[i+2]) * (tmp1[i+2] - tmp2[i+2]);
		}
		
		return Math.sqrt(sum_r) + Math.sqrt(sum_g) + Math.sqrt(sum_b);
	}
	
	public static void displayImages() {
		/*new LoadImage("image/resultats/"+image+"_original.png", m_draw, "Original", false, 0, 0);
		new LoadImage("image/resultats/"+image+"_flou.png", m, "Flou", false, 320, 0);
		new LoadImage("image/resultats/"+image+"_outline.png", outline, "Contours", true, 640, 0);
		new LoadImage("image/resultats/"+image+"_grad_angle.png", grad_angle, "Direction du gradient", true, 0, 240);
		new LoadImage("image/resultats/"+image+"_acc.png", accumulation, "Accumulation", false, 320, 240);*/
	}
	
	public static double getRadian(double degree) {
		return (Math.PI / 180.0) * degree;
	}
	
	public static void main(String[] args) {
		/*
		 * Image en base
		 */
		
		String image = "cam_01";
		
		FormDetector fd = new FormDetector(image);
		
		long start = System.currentTimeMillis();
		Point[] eye_zone = findFace(image);
		System.out.println("Found face in\t"+(System.currentTimeMillis()-start)+"ms");
		
		start = System.currentTimeMillis();
		Point[] eye = findEye(image, eye_zone);
		System.out.println("Found eye in\t"+(System.currentTimeMillis()-start)+"ms");

		start = System.currentTimeMillis();
		String histo1 = computeHisto(fd.getUntouched(), eye);
		System.out.println("Histogram in\t"+(System.currentTimeMillis()-start)+"ms");
		
		
		/*
		 * Snapshot.
		 */
		
		String image2 = "cam_03";
		
		FormDetector fd2 = new FormDetector(image2);
		
		start = System.currentTimeMillis();
		Point[] eye_zone2 = findFace(image2);
		System.out.println("Found face in\t"+(System.currentTimeMillis()-start)+"ms");
		
		start = System.currentTimeMillis();
		Point[] eye2 = findEye(image2, eye_zone2);
		System.out.println("Found eye in\t"+(System.currentTimeMillis()-start)+"ms");

		start = System.currentTimeMillis();
		String histo2 = computeHisto(fd2.getUntouched(), eye2);
		System.out.println("Histogram in\t"+(System.currentTimeMillis()-start)+"ms");
		
		
		/*
		 * Comparaison.
		 */
		
		double dist = compareHistoEuclid(histo1, histo2);
		System.out.println("Distance: "+dist);
		
		/*//double[] lut = FormDetector.computeLUT("image/circle_BW.png");
		double[] lut = FormDetector.computeLUT("image/Elipse.png");

		String image = "cam_01";
		
		Mat m = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_GRAYSCALE);
		m.convertTo(m, CvType.CV_64FC1);
		
		Mat m_draw = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_COLOR);
		Mat m_original = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_GRAYSCALE);
		
		Size size = new Size(m.size().width, m.size().height);
		
		// espace de données entre 0 et 1
		//for(int i=0; i<size.height; i++) {
		//	for(int j=0; j<size.width; j++) {
		//		m.put(i, j, m.get(i, j)[0]/255);
		//	}
		//s}
		
		// flouter
		Imgproc.GaussianBlur(m, m, new Size(3, 3), 0, 0);
		
		// calculer les contours
		List<MatOfPoint> liste_contours = new ArrayList<MatOfPoint>();
		Imgproc.findContours(m_original, liste_contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_NONE);
		MatOfPoint contour = liste_contours.get(0);
		
		// calculer les gradients
		Mat grad_x = Mat.zeros(size, CvType.CV_64FC1);
		Mat grad_y = Mat.zeros(size, CvType.CV_64FC1);
		for(int i=1; i<size.height-1; i++) {
			for(int j=1; j<size.width-1; j++) {
				grad_y.put(i, j, m.get(i+1, j)[0] - m.get(i-1, j)[0]);
				grad_x.put(i, j, m.get(i, j+1)[0] - m.get(i, j-1)[0]);
			}
		}
		
		// parcourir les contours
		// pour chaque point calculer l'angle (direction du gradient)
		Mat accumulation = Mat.zeros(size, CvType.CV_64FC1);
		Mat outline = Mat.zeros(size, CvType.CV_64FC1);
		Mat grad_angle = Mat.zeros(size, CvType.CV_64FC1);
		double r_min = 100;
		double r_max = 200;
		//for(int i=0; i<contour.size().height; i++) {
		for(int i=0; i<size.height; i++) {
			for(int j=0; j<size.width; j++) {
				//double ix = -grad_x.get(i, j)[0];
				//double iy = -grad_y.get(i, j)[0];
				double ix = grad_x.get(i, j)[0];
				double iy = grad_y.get(i, j)[0];

				if(Math.pow(ix, 2) + Math.pow(iy, 2) > 200) {
					System.out.println(Math.pow(ix, 2) + Math.pow(iy, 2));
					//int x = (int) contour.get(i, 0)[0];
					//int y = (int) contour.get(i, 0)[1];
					outline.put(i, j, 1);
	    			//System.out.println("cord:\t"+i+" "+j);
					
					double grad_norm_angle = Math.atan2(iy,ix)/(2*Math.PI);
					grad_norm_angle = (grad_norm_angle+1) % (double) 1;
					int grad_angle_in_degree = (int) (grad_norm_angle*360);
					
					grad_angle.put(i, j, grad_norm_angle);
					
					// chercher l'angle beta avec la LUT
					double beta = getRadian(lut[grad_angle_in_degree]);
					//System.out.println("beta:\t"+lut_cercle[grad_angle_in_degree]+"\t"+beta);
					//double r_min = 150;
					//double r_max = 200;
					
					for(int k=(int) r_min; k<r_max; k++) {
						int x = (int) (j + Math.cos(beta) * k);
						int y = (int) (i + Math.sin(beta) * k);
						
						if(x >= 0 && y >= 0 && x < size.width && y < size.height) {
							//System.out.println(x+" "+y);
							accumulation.put(y, x, accumulation.get(y, x)[0]+1);
						}
					}
				}
			}
		}

		MinMaxLocResult max = Core.minMaxLoc(accumulation);
		Core.circle(m_draw, max.maxLoc, 1, new Scalar(0, 0, 255), 3);
		
		// cercle
		//int rect_size = (int) r_max;
		//Point rect_1 = new Point(max.maxLoc.x-rect_size, max.maxLoc.y-rect_size);
		//Point rect_2 = new Point(max.maxLoc.x+rect_size, max.maxLoc.y+rect_size);
		//Core.rectangle(m_draw, rect_1, rect_2, new Scalar(0, 0, 255));
		
		// elipse
		int rect_size = (int) r_max;
		Point rect_1 = new Point(max.maxLoc.x-(rect_size/2), max.maxLoc.y-rect_size);
		Point rect_2 = new Point(max.maxLoc.x+(rect_size/2), max.maxLoc.y+rect_size);
		Core.rectangle(m_draw, rect_1, rect_2, new Scalar(0, 0, 255)); // visage
		rect_1 = new Point(max.maxLoc.x-(rect_size/2), max.maxLoc.y-(rect_size/4));
		rect_2 = new Point(max.maxLoc.x+(rect_size/2), max.maxLoc.y+(rect_size/4));
		Core.rectangle(m_draw, rect_1, rect_2, new Scalar(0, 255, 0)); // yeux
		
		// tarcer la droite dans la matrice d'accumulation  entre r min et r max
		// pour r de min à max, tracer les points
		new LoadImage("image/resultats/"+image+"_original.png", m_draw, "Original", false, 0, 0);
		new LoadImage("image/resultats/"+image+"_flou.png", m, "Flou", false, 320, 0);
		new LoadImage("image/resultats/"+image+"_outline.png", outline, "Contours", true, 640, 0);
		new LoadImage("image/resultats/"+image+"_grad_angle.png", grad_angle, "Direction du gradient", true, 0, 240);
		new LoadImage("image/resultats/"+image+"_acc.png", accumulation, "Accumulation", false, 320, 240);*/
		
	}

}
